package de.justphil.tcg.tcgserver.game.mechanics.property.properties;

import java.util.List;

import de.justphil.tcg.tcgserver.commons.exceptions.TCGException;
import de.justphil.tcg.tcgserver.game.mechanics.config.Config;
import de.justphil.tcg.tcgserver.game.mechanics.event.Event;
import de.justphil.tcg.tcgserver.game.mechanics.game.Attack;
import de.justphil.tcg.tcgserver.game.mechanics.game.Game;
import de.justphil.tcg.tcgserver.game.mechanics.monster.Monster;
import de.justphil.tcg.tcgserver.game.mechanics.monster.MonsterList;
import de.justphil.tcg.tcgserver.game.mechanics.property.Property;
import org.slf4j.Logger;
import static de.justphil.tcg.tcgserver.commons.logging.TCGLogging.*;

public class Attacker extends Property {

	private static final Logger log = gameLogger(Property.class);

	public Attacker(Game game) {

		super(game);
	}

	@Override
	public int getType() {

		return PROPERTY_ATTACKER;
	}

	@Override
	protected MonsterList handleEvent(Event event) {

		int eventType = event.getType();

		switch (eventType) {
		case Event.EVENT_ATTACK:

			MonsterList monsters = new MonsterList();
			monsters.add(handleAttack((Attack) event.getData()));
			return monsters;

		default:
			assert false;
			return null;
		}
	}

	@Override
	protected List<Integer> getListeningEventTypes() {

		List<Integer> types = super.getListeningEventTypes();
		types.add(Event.EVENT_ATTACK);

		return types;
	}

	private Monster handleAttack(Attack attack) {

		Monster monster = getMonster();

		if (attack.getAttacker().equals(monster)) {

			attack(attack);
			return attack.getTarget();
		}
		return null;
	}

	private void attack(Attack attack) {

		Monster attacker = attack.getAttacker();
		Monster target = attack.getTarget();

		log.info("Attack: ({}).", new Object[] { attack });

		double targetSpeed = target.getSpeed();
		double attackerSpeed = attacker.getSpeed()
				* Config.ATTACKER_AGILITY_BONUS;

		double chanceToHit;
		if (attackerSpeed > 0) {

			if (targetSpeed > 0) {

				chanceToHit = 1.0 / (1.0 + targetSpeed / attackerSpeed);
			} else {

				chanceToHit = 1.0;
			}
		} else {
			chanceToHit = 0.0;
		}

		log.debug("    chance to hit: {}.", new Object[] { chanceToHit });

		boolean hit = true;

		if (getGameRandom().nextDouble() > chanceToHit) {

			hit = false;
		}

		int targetDefense = target.getDefense();
		int attackerAttack = attacker.getAttack();

		double damage;
		if (attackerAttack > 0) {

			if (targetDefense > 0) {

				damage = attackerAttack
						/ (1.0 + (double) targetDefense
								/ (double) attackerAttack);
			} else {

				damage = attackerAttack;
			}
		} else {
			damage = 0;
		}

		int intDamage = (int) Math.ceil(damage);

		log.debug("    potential damage: {} ({}).", new Object[] { intDamage,
				damage });

		if (hit) {

			log.info("    dealed damage: {}.", new Object[] { intDamage });

			try {
				getScheduler().scheduleFirst(
						new Event(Event.EVENT_MONSTER_HIT, attack), 0.0);
			} catch (TCGException e) {
				assert false: e.getMessage();
			}

			for (int i = 0; i < intDamage; i++) {

				provideProperty(target, PROPERTY_DAMAGE);
			}

		} else {

			log.info("    dealed damage: {}.", new Object[] { 0 });

			Event event = new Event(Event.EVENT_MISS, attack);
			try {
				getScheduler().scheduleFirst(event, 0.0);
			} catch (TCGException e) {
				e.printStackTrace();
			}
		}
	}
}
